/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package eu.fastl.dragster.aop;

import org.aspectj.lang.ProceedingJoinPoint;

import eu.fastl.dragster.exception.MonitoringExceptionHandlerProvider;
import eu.fastl.dragster.model.ExecutionRecord;
import eu.fastl.dragster.model.ExecutionRecordFactoryProvider;
import eu.fastl.dragster.monitoring.IdStorageProvider;
import eu.fastl.dragster.monitoring.MonitoringServiceProvider;

/**
 * Aspect with an around advice for measuring method executions. It will
 * participate in any unique ID set in the storage or create a new one if
 * necessary
 *
 * @author Ernst Fastl
 */
public class MonitoringAspect {

    public static final String DEFAULT_MONITORING_TYPE = "METHOD_INVOCATION";
    private String type = DEFAULT_MONITORING_TYPE;
    private boolean includeParametersInDetail = true;

    public void setType(String type) {
        this.type = type;
    }

    public void setIncludeParametersInDetail(boolean includeParametersInDetail) {
        this.includeParametersInDetail = includeParametersInDetail;
    }

    public Object measureMethodExecution(ProceedingJoinPoint pJoinPoint) throws Throwable {
        boolean createdId = IdStorageProvider.createIdIfNecessary();

        long start = System.currentTimeMillis();

        Object retval = null;
        try {

            retval = pJoinPoint.proceed();
            ExecutionRecord executionRecord = createExecutionRecord(pJoinPoint, start);
            MonitoringServiceProvider.getMonitoringService().recordExecutionTime(executionRecord);

        } catch (Throwable exception) {
            ExecutionRecord executionRecord = createExecutionRecord(pJoinPoint, start);
            MonitoringExceptionHandlerProvider.getMonitoringExceptionHandler().handleException(exception,
                    executionRecord);
        } finally {
            // If this Intercepor Call has created the current ID we need to
            // reset it to null in case this thread gets reused
            if (createdId) {
                IdStorageProvider.getIdStorage().setId(null);
            }
        }
        return retval;
    }

    private ExecutionRecord createExecutionRecord(ProceedingJoinPoint pJoinPoint, long start) {
        long end = System.currentTimeMillis();
        String detail = getDetailString(pJoinPoint);
        ExecutionRecord executionRecord = ExecutionRecordFactoryProvider.getExecutionRecordFactory().create(
                end - start, IdStorageProvider.getId(), this.type, detail);
        return executionRecord;
    }

    private String getDetailString(ProceedingJoinPoint pJoinPoint) {
        StringBuilder retval = new StringBuilder();
        retval.append(pJoinPoint.getSignature().toShortString());
        if (this.includeParametersInDetail) {
            Object[] arguments = pJoinPoint.getArgs();
            for (int i = 0; i < arguments.length; i++) {
                retval.append("{");
                retval.append(arguments[i]);
                retval.append("}");
            }
        }
        return retval.toString();
    }
}
